package com.enation.app.javashop.core.system.service.impl;

import com.alibaba.fastjson.JSON;
import com.dag.eagleshop.core.account.utils.HttpUtils;
import com.enation.app.javashop.core.base.rabbitmq.TimeExecute;
import com.enation.app.javashop.core.member.model.dos.ConnectDO;
import com.enation.app.javashop.core.member.model.dos.Member;
import com.enation.app.javashop.core.member.model.enums.CommissionTypeEnum;
import com.enation.app.javashop.core.member.model.enums.ConnectTypeEnum;
import com.enation.app.javashop.core.member.service.ConnectManager;
import com.enation.app.javashop.core.member.service.MemberManager;
import com.enation.app.javashop.core.statistics.util.WeChatUtil;
import com.enation.app.javashop.core.system.enums.WeChatPublicConstant;
import com.enation.app.javashop.core.system.enums.WechatMiniproTemplateTypeEnum;
import com.enation.app.javashop.core.system.model.dos.WechatMsgTemplate;
import com.enation.app.javashop.core.system.model.dos.WxPublicTemplate;
import com.enation.app.javashop.core.system.model.dos.WxPublicTemplateData;
import com.enation.app.javashop.core.system.model.dto.MiniproMsgDataDTO;
import com.enation.app.javashop.core.system.model.dto.MiniproSendMsgDTO;
import com.enation.app.javashop.core.system.model.dto.WeChatPublicToken;
import com.enation.app.javashop.core.system.model.dto.WeChatPublicUserInfo;
import com.enation.app.javashop.core.system.sendMessage.WechatSendMessage;
import com.enation.app.javashop.core.system.service.WechatPublicManager;
import com.enation.app.javashop.core.trade.order.model.dos.OrderDO;
import com.enation.app.javashop.framework.database.DaoSupport;
import com.enation.app.javashop.framework.trigger.Interface.TimeTrigger;
import com.enation.app.javashop.framework.util.CurrencyUtil;
import com.enation.app.javashop.framework.util.DateUtil;
import com.enation.app.javashop.framework.util.EnvironmentUtils;
import com.enation.app.javashop.framework.util.StringUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author 王志杨
 * @since 2020/11/10 10:38
 * 微信公众号业务层实现
 */
@Service
public class WechatPublicManagerImpl implements WechatPublicManager {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ConnectManager connectManager;
    @Autowired
    @Qualifier("memberDaoSupport")
    private DaoSupport memberDaoSupport;
    @Autowired
    private MemberManager memberManager;
    @Autowired
    @Qualifier("systemDaoSupport")
    private DaoSupport daoSupport;
    @Autowired
    private TimeTrigger timeTrigger;

    // 公众号延迟发送队列前缀名
    private static final String PUBLIC_TRIGGER_PREFIX = "PUBLIC_MESSAGE_";

    // 小程序延迟发送队列前缀名
    private static final String TRIGGER_PREFIX = "MINIPRO_MESSAGE_";

    private final Log logger = LogFactory.getLog(this.getClass());

    // 凭证token获取（GET）
    private final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    //获取用户个人信息（GET）
    private final static String user_info_url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
    //公众号发送模板消息（POST）
    private final static String send_template_message = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
    //公众号发送模板消息的链接地址
//    private final static String link_url = "http://mp.weixin.qq.com/";

    @Override
    public String processRequest(HttpServletRequest request) {
        // xml格式的消息数据
        String respXml = null;
        // 默认返回的文本消息内容
        String respContent = null;
        try {
            // 调用parseXml方法解析请求消息
            Map<String,String> requestMap = WeChatUtil.parseXml(request);
            logger.info("关注/取关公众号消息：" + JSON.toJSON(requestMap));
            // 消息类型
            String msgType = requestMap.get(WeChatPublicConstant.MsgType);
            if (msgType.equals(WeChatPublicConstant.REQ_MESSAGE_TYPE_EVENT)) {
                // 事件类型
                String eventType = requestMap.get(WeChatPublicConstant.Event);
                // 关注
                if (eventType.equals(WeChatPublicConstant.EVENT_TYPE_SUBSCRIBE)) {
                    //欢迎订阅语句
                    respContent = WeChatPublicConstant.WELCOME_TEXT;
                    respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
                    //关注后获取用户在公众号的信息：openID unionID ，并保存到数据库
                    WeChatPublicUserInfo userInfo = this.getUserInfo(requestMap.get(WeChatPublicConstant.FromUserName));
                    if (ObjectUtils.isEmpty(userInfo)) {
                        logger.error("微信公众号获取用户个人信息失败");
                    }
                    ConnectDO connectDO = connectManager.getConnectByUnionId(userInfo.getUnionid());
                    String publicOpenId = userInfo.getOpenid();
                    if (ObjectUtils.isEmpty(connectDO)) {
                        connectDO = new ConnectDO();
                        connectDO.setUnionId(userInfo.getUnionid());
                        // 设置公众号openID
                        connectDO.setPublicOpenId(publicOpenId);
                        connectDO.setUnionType(ConnectTypeEnum.WECHAT.value());
                        connectDO.setUnboundTime(DateUtil.getDateline());
                        this.memberDaoSupport.insert(connectDO);
                    } else {
                        // 设置公众号openID
                        connectDO.setPublicOpenId(publicOpenId);
                        connectDO.setUnboundTime(DateUtil.getDateline());
                        this.memberDaoSupport.update(connectDO, connectDO.getId());
                    }
                }
                // 取消关注
                else if (eventType.equals(WeChatPublicConstant.EVENT_TYPE_UNSUBSCRIBE)) {
                    WeChatPublicUserInfo userInfo = this.getUserInfo(requestMap.get(WeChatPublicConstant.FromUserName));
                    if (ObjectUtils.isEmpty(userInfo)) {
                        logger.error("微信公众号取关获取用户个人信息失败");
                    }
                    ConnectDO connectDO = connectManager.getConnectByUnionId(userInfo.getUnionid());
                    if (!ObjectUtils.isEmpty(connectDO)) {
                        connectDO.setPublicOpenId("");
                        connectDO.setUnboundTime(DateUtil.getDateline());
                        this.memberDaoSupport.update(connectDO, connectDO.getId());
                        logger.info("取关公众号处理成功" + connectDO.getMemberId());
                    }else{
                        logger.error("没有找到此公众号对应的用户" + connectDO.getMemberId());
                    }
                }
            }
            respContent = StringUtil.isEmpty(respContent) ? "公众号正在建设中，敬请期待..." : respContent;
            if(respXml == null){
                respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
            }
            return respXml;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 获取接口访问凭证
     */
    @Override
    public String getTokenFromWeiXin() {
        if (!EnvironmentUtils.isProd()) {
            logger.error("accessToken刷新失败，非生产环境无法主动获取公众号access_token");
            return null;
        }
        String requestUrl = token_url.replace("APPID", WeChatPublicConstant.appID).replace("APPSECRET", WeChatPublicConstant.appsecret);
        // 发起GET请求获取凭证
        WeChatPublicToken weChatPublicToken = HttpUtils.httpGetByJson(requestUrl, WeChatPublicToken.class);
        return weChatPublicToken.getAccessToken();
    }

    @Override
    public WeChatPublicUserInfo getUserInfo(String openId) {
        String token = this.getTokenFromCache();
        if (StringUtil.isEmpty(token)) {
            logger.error("公众号粉丝信息获取失败，公众号access_token获取为空");
            throw new RuntimeException("公众号access_token获取失败");
        }
        String requestUrl = user_info_url.replace("ACCESS_TOKEN", token).replace("OPENID", openId);
        // 发起GET请求获取凭证
        return HttpUtils.httpGetByJson(requestUrl, WeChatPublicUserInfo.class);
    }

    @Override
    public String getTokenFromCache() {
        String token = stringRedisTemplate.opsForValue().get(WeChatPublicConstant.WEIXIN_PUBLIC_TOKEN);
        if(StringUtil.isEmpty(token)){
            token = this.getTokenFromWeiXin();
            if(token != null){
                stringRedisTemplate.opsForValue().set(WeChatPublicConstant.WEIXIN_PUBLIC_TOKEN, token, Duration.ofSeconds(7000L));
            }
        }
        return token;
    }

    @Override
    public void publicSendMessage(WxPublicTemplate wxPublicTemplate) {
        //1.验证接收消息人的公众号open_id，这里的toUser指的是公众号的openId
        if(StringUtil.isEmpty(wxPublicTemplate.getTouser())){
            logger.error("发送微信公众号消息失败，接收消息人的公众号open_id为空");
            return;
        }

        //2.验证公众号消息模板是否可用
        String template_id = wxPublicTemplate.getTemplate_id();
        String sql = "select * from es_wechat_msg_template where template_id = ?";
        WechatMsgTemplate publicTemplate = this.daoSupport.queryForObject(sql, WechatMsgTemplate.class, template_id);
        if (publicTemplate == null || publicTemplate.getIsOpen().equals(0)) {
            logger.error("发送微信公众号消息失败，消息模板为空或未开启");
            return;
        }

        //3.获取公众号access_token，拼接微信公众号请求地址
        String token = stringRedisTemplate.opsForValue().get(WeChatPublicConstant.WEIXIN_PUBLIC_TOKEN);
        if (StringUtil.isEmpty(token)) {
            logger.error("公众号发送模板信息失败，公众号access_token获取为空");
            return;
        }
        String url = send_template_message.replace("ACCESS_TOKEN", token);

        //4.封装请求微信公众号发送模板消息接口的参数
        // 设置公众号模板参数-标题字体颜色-链接地址(小程序)
        wxPublicTemplate.setTopcolor(WeChatPublicConstant.TOP_COLOR_RED);
        // 设置小程序的appid和转发的页面
        // wxPublicTemplate.setUrl(link_url);这个url是跳转的页面，跳转到小程序不需要加
        Map<String, String> miniPrograms = new HashMap<>();
        Map configMap = connectManager.initConnectSetting();
        miniPrograms.put("appid", (String)configMap.get("wechat_miniprogram_app_id"));
        miniPrograms.put("pagepath", WechatMiniproTemplateTypeEnum.PUBLIC_PROFITE_NOTICE.getPage());// 注意，这里是支持传参的！！！
        wxPublicTemplate.setMiniprogram(miniPrograms);

        //5.发送请求
        String uuid = UUID.randomUUID().toString().replace("-", "");
        logger.debug("公众号发送模板消息请求报文" + uuid + ":" + JSON.toJSON(wxPublicTemplate).toString());
        Map map = HttpUtils.httpPostByJson(url, wxPublicTemplate, Map.class);
        logger.debug("公众号发送模板消息响应报文" + uuid + ":" + map.toString());
        String errMsg = map.get("errmsg").toString();
        if (!"ok".equals(errMsg) && logger.isDebugEnabled()) {
            logger.debug("公众号发送模板消息错误，返回内容：" + map.toString());
        }

    }



    // 给推广人发送微信订阅消息（2020-11-13暂定使用，改为微信公众号发送消息）
    @Deprecated
    @Override
    public void sendExtensionMessage(OrderDO orderDO, BigDecimal commissionMoney, Integer memberIdLv1, WechatMiniproTemplateTypeEnum templateTypeEnum, Long startTime,
                                     Integer spreadWay, String orderTypeName, String commissionTypeName ){
        String tradeSn = orderDO.getTradeSn();
        Integer memberId = orderDO.getMemberId();
        Member member = memberManager.getModel(memberId);
        ConnectDO connect = connectManager.getConnect(memberIdLv1, ConnectTypeEnum.WECHAT.value());
        if(connect == null){
            logger.warn("小程序消息发送失败，未找到对应的小程序openid");
            return;
        }

        // 封装data参数
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("character_string3", MiniproMsgDataDTO.build(orderDO.getSn()));
        // 下单用户
        dataMap.put("thing7", MiniproMsgDataDTO.build(member.getNickname()));
        //订单金额
        dataMap.put("amount2", MiniproMsgDataDTO.build(orderDO.getPayMoney().toString()));
        //预估佣金
        dataMap.put("amount6", MiniproMsgDataDTO.build(commissionMoney.toString()));
        //备注 你获得了一笔+${推广类型}+的+${订单类型}+${佣金类型}1直推 2间推
        String spreadWayName = spreadWay == 1 ?"直推" :"间推";
        dataMap.put("thing5", MiniproMsgDataDTO.build("你获得了一笔"+spreadWayName+"的"+orderTypeName+commissionTypeName));

        // 封装消息
        MiniproSendMsgDTO miniproSendMsgDTO = new MiniproSendMsgDTO(connect.getOpenId(), templateTypeEnum,
                dataMap, "");

        // 延迟发送
        timeTrigger.add(TimeExecute.MINIPRO_MESSAGE_EXECUTER, miniproSendMsgDTO , startTime, TRIGGER_PREFIX + tradeSn);
    }


    /**
     * 微信公众号给推广人发送收益消息
     * @param orderDO 订单实体
     * @param commissionMoney 预估佣金
     * @param memberIdLv1 消息接收方的memberId
     * @param templateTypeEnum 消息模板
     * @param startTime 消息发送时间
     * @param spreadWay 推广方式
     * @param commissionTypeName 佣金类型
     */
    @Override
    public void sendProfitMessageFromPublic(OrderDO orderDO, BigDecimal commissionMoney,Integer memberIdLv1,
                                            WechatMiniproTemplateTypeEnum templateTypeEnum, Long startTime,
                                            Integer spreadWay, String commissionTypeName ){
        //1.获取并校验消息接收方的公众号open_id
        String tradeSn = orderDO.getTradeSn();
        Integer memberId = orderDO.getMemberId();
        Member member = memberManager.getModel(memberId);
        ConnectDO connect = connectManager.getConnect(memberIdLv1, ConnectTypeEnum.WECHAT.value());
        if(connect == null || StringUtil.isEmpty(connect.getPublicOpenId())){
            logger.error("微信公众号消息发送失败，未找到对应的公众号openid");
            return;
        }

        //2.使用枚举查看公众号消息模板并校验
        String sql = "select * from es_wechat_msg_template where tmp_type = ?";
        WechatMsgTemplate publicTemplate = this.daoSupport.queryForObject(sql, WechatMsgTemplate.class, templateTypeEnum.value());
        //判断公众号消息模板是否开启，开启则发消息
        if (publicTemplate == null || publicTemplate.getIsOpen().equals(0)) {
            logger.error("发送微信公众号消息失败，消息模板为空或未开启");
            return;
        }

        //3.封装data参数
        Map<String, WxPublicTemplateData> dataMap = new HashMap<>();
        // 下单用户--->红色
        String spreadWayName = spreadWay == 1 ?"直推的" :"间推的";
        if (CommissionTypeEnum.SUBSIDY.getText().equals(commissionTypeName)) {
            spreadWayName = "";
        }
        dataMap.put("first", new WxPublicTemplateData(templateTypeEnum.getFirst()
                .replace("NICKNAME", member.getNickname())
                .replace("SPREAD_WAY", spreadWayName)
                .replace("COMMISSION_TYPE", commissionTypeName), WeChatPublicConstant.TOP_COLOR_BLUE));
        //订单号
        dataMap.put("keyword1", new WxPublicTemplateData(orderDO.getSn(), WeChatPublicConstant.TOP_COLOR_BLUE));
        //订单金额为支付金额减去运费（这部分是计算佣金的）
        double payMoneyExcludeShip = CurrencyUtil.sub(orderDO.getPayMoney(), orderDO.getShippingPrice());
        payMoneyExcludeShip = payMoneyExcludeShip > 0 ? payMoneyExcludeShip : 0.0d;
        dataMap.put("keyword2", new WxPublicTemplateData(payMoneyExcludeShip + "元", WeChatPublicConstant.TOP_COLOR_BLUE));
        //预估佣金
        dataMap.put("keyword3", new WxPublicTemplateData(commissionMoney.toString().concat("元"), WeChatPublicConstant.TOP_COLOR_BLUE));
        //备注--->红色  订单取消/退款会导致佣金变化，实际收益金额以客户收货为准
        String remark = templateTypeEnum.getRemark();
        if (CommissionTypeEnum.SUBSIDY.getText().equals(commissionTypeName)) {
            remark = "订单退款会导致补贴失效，实际收益金额以客户收货为准>>";
        }
        dataMap.put("remark", new WxPublicTemplateData(remark, WeChatPublicConstant.TOP_COLOR_RED));

        //4.封装消息（微信公众号发消息）
        WxPublicTemplate wxPublicTemplate = new WxPublicTemplate(publicTemplate.getTemplateId(), connect.getPublicOpenId(),
                null, null, null, dataMap);

        //5.延迟发送
        timeTrigger.add(TimeExecute.PUBLIC_MESSAGE_EXECUTER, wxPublicTemplate , startTime, PUBLIC_TRIGGER_PREFIX + tradeSn);
    }
    @Override
    public void sendRecommendedCouponsMessageFromPublic(Member member,WechatMiniproTemplateTypeEnum templateTypeEnum,Integer myFriendsRegisterReward){
        WechatSendMessage.WX_MESSAGE_EXECUTOR.execute(() -> {
            //1.获取并校验消息接收方的公众号open_id
            Integer memberId = member.getMemberId();
            ConnectDO connect = connectManager.getConnect(memberId, ConnectTypeEnum.WECHAT.value());
            if(connect == null || StringUtil.isEmpty(connect.getPublicOpenId())){
                logger.error("微信公众号消息发送失败，未找到对应的公众号openid");
                return;
            }

            //2.使用枚举查看公众号消息模板并校验
            String sql = "select * from es_wechat_msg_template where tmp_type = ?";
            WechatMsgTemplate publicTemplate = this.daoSupport.queryForObject(sql, WechatMsgTemplate.class, templateTypeEnum.value());
            //判断公众号消息模板是否开启，开启则发消息
            if (publicTemplate == null || publicTemplate.getIsOpen().equals(0)) {
                logger.error("发送微信公众号消息失败，消息模板为空或未开启");
                return;
            }

            //3.封装data参数
            Map<String, WxPublicTemplateData> dataMap = new HashMap<>();
            // 标题
            dataMap.put("first", new WxPublicTemplateData(templateTypeEnum.getFirst()
                    .replace("MYFRIENDSREGISTERREWARD", myFriendsRegisterReward.toString()), WeChatPublicConstant.TOP_COLOR_BLUE));
            //昵称
            dataMap.put("keyword1", new WxPublicTemplateData(member.getNickname(), WeChatPublicConstant.TOP_COLOR_BLUE));
            // 账户
            dataMap.put("keyword2", new WxPublicTemplateData(member.getUname(), WeChatPublicConstant.TOP_COLOR_BLUE));
            // 注册时间
            dataMap.put("keyword3", new WxPublicTemplateData(DateUtil.toString(member.getCreateTime(), "yyyy-MM-dd HH:mm:ss"), WeChatPublicConstant.TOP_COLOR_BLUE));
            // 备注
            dataMap.put("remark", new WxPublicTemplateData(templateTypeEnum.getRemark(), WeChatPublicConstant.TOP_COLOR_RED));

            //4.封装消息（微信公众号发消息）
            WxPublicTemplate wxPublicTemplate = new WxPublicTemplate(publicTemplate.getTemplateId(), connect.getPublicOpenId(),
                    null, null, null, dataMap);

            //5.延迟发送
            timeTrigger.add(TimeExecute.PUBLIC_MESSAGE_EXECUTER, wxPublicTemplate , DateUtil.getDateline(), PUBLIC_TRIGGER_PREFIX + DateUtil.getDateline()+StringUtil.getRandStr(4));

        });
        }



}
